#include "StdAfx.h"
#include "GrassLoader.h"
#include "PagedBillboardBlock.h"

using namespace std;

namespace FEngine
{

	GrassLoader::GrassLoader(float _blockSize, float _viewRange)
		: mBlockSize(_blockSize)
	{
		mBillboardTexture		= NULL;
		mVertexBuffer			= NULL;
		mIndexBuffer			= NULL;
		mVertexDeclaration		= NULL;
		mMaximumViewDistance	= _viewRange;
		if (mMaximumViewDistance <0)
			mMaximumViewDistance = 0;

		mQuadTreeManager = new QuadTreeManager(_blockSize);

		// Load the vertex declaration
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexDeclaration(VertexDeclarationBillboard, &mVertexDeclaration);
	}

	GrassLoader::~GrassLoader()
	{
		if (mVertexBuffer)
			mVertexBuffer->Release();
		if (mVertexDeclaration)
			mVertexDeclaration->Release();
		if (mIndexBuffer)
			mIndexBuffer->Release();
		if (mBillboardTexture)
			mBillboardTexture->Release();
		if (mQuadTreeManager)
			delete mQuadTreeManager;
	}
	
	void GrassLoader::calculateStats()
	{
		mQuadTreeManager->_getListNodes();
		
		mNumberPage = mQuadTreeManager->getNumberNodes();

		mNumberObject = 0;
		std::list<QuadTreeNode*> list = mQuadTreeManager->getNodes();
		for (std::list<QuadTreeNode*>::const_iterator it = list.begin(); it != list.end(); ++it)
			mNumberObject += ((PagedBillboardBlock*)(*it))->getNumberObject();
	}

	void GrassLoader::createBillboard(const std::string& _textureName)
	{
		// Set the texture
		std::wstring stemp = std::wstring(_textureName.begin(), _textureName.end());
		LPCWSTR fileName = stemp.c_str();

		if (D3DXCreateTextureFromFileEx(CoreEngine::getInstance()->getDeviceInterface(), fileName, D3DX_DEFAULT, D3DX_DEFAULT, 
									D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 
									NULL, NULL, &mBillboardTexture) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Cannot load the texture file");
	}
	
	void GrassLoader::addBillboard(const Vector3& _pos, const Vector3& _sca, float _windForce, float _windOffset)
	{
		PagedBillboardBlock* obj = (PagedBillboardBlock*)mQuadTreeManager->getNodeByPosition(Vector2(_pos.x, _pos.z),
																								new PagedBillboardBlock((float)mBlockSize, mMaximumViewDistance));
		obj->addBillboard(_pos, _sca, _windForce, _windOffset);
	}

	int	GrassLoader::getNumberObject() const
	{
		return mNumberObject;
	}

	int	GrassLoader::getNumberVisibleObject() const
	{
		int number = 0;
		
		std::list<QuadTreeNode*> list = mQuadTreeManager->getVisibleNodes();
		for (std::list<QuadTreeNode*>::const_iterator it = list.begin(); it != list.end(); ++it)
			number += ((PagedBillboardBlock*)(*it))->getNumberVisibleObject();

		return number;
	}

	int	GrassLoader::getNumberPage() const
	{
		return mNumberPage;
	}

	int	GrassLoader::getNumberVisiblePage() const
	{
		int number = 0;
		
		return mQuadTreeManager->getNumberVisibleNodes();

		return number;
	}
			
	void GrassLoader::update(double _elapsedTime)
	{
		float camPosY = CoreEngine::getInstance()->getCamera()->getPosition().y;
		float viewDist = sqrt((mMaximumViewDistance * mMaximumViewDistance) + (camPosY * camPosY));
		mQuadTreeManager->checkVisibility(viewDist);
		std::list<QuadTreeNode*> list = mQuadTreeManager->getVisibleNodes();
		for (std::list<QuadTreeNode*>::const_iterator it = list.begin(); it != list.end(); ++it)
			((PagedBillboardBlock*)(*it))->_checkVisibleObjects();
	}

	void GrassLoader::_setBuffers()
	{
		// Release the previous buffers
		if (mVertexBuffer)
			mVertexBuffer->Release();
		mVertexBuffer = NULL;

		if (mIndexBuffer)
			mIndexBuffer->Release();
		mIndexBuffer = NULL;
		
		// Get the list of vertices
		vector<FE_BILLBOARD*> vectorObjectInstance;
		
		std::list<QuadTreeNode*> list = mQuadTreeManager->getVisibleNodes();
		for (std::list<QuadTreeNode*>::const_iterator it = list.begin(); it != list.end(); ++it)
			((PagedBillboardBlock*)(*it))->getVectorObjectInstance(&vectorObjectInstance);

		mNumberVertices = vectorObjectInstance.size();
		mNumberPrimitives = mNumberVertices / 2;
		int numberBillboard = mNumberPrimitives / 2;
		
		if (vectorObjectInstance.size() == 0)
			return;
		
		unsigned int verticesSize = mNumberVertices * sizeof(FE_BILLBOARD);
		unsigned int indexesSize = numberBillboard * 6 * sizeof(short);
		
		// Create the vertex buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexBuffer(verticesSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &mVertexBuffer, NULL);
		
		// Create the index buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateIndexBuffer(indexesSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIndexBuffer, NULL);
		
		
		// Copy the vertices into memory
		FE_BILLBOARD* vertices;
		mVertexBuffer->Lock(0, 0, (void**)&vertices, 0);
		for (int n = 0; n < mNumberVertices; ++n)
			vertices[n] = *vectorObjectInstance[n];
		mVertexBuffer->Unlock();
		
		// Copy the indexes into memory
		short* indexes;
		mIndexBuffer->Lock(0, 0, (void**)&indexes, 0);
		for (int n = 0; n < numberBillboard; ++n)
		{
			indexes[6 * n + 0] = 4 * n + 0;
			indexes[6 * n + 1] = 4 * n + 1;
			indexes[6 * n + 2] = 4 * n + 2;
			indexes[6 * n + 3] = 4 * n + 0;
			indexes[6 * n + 4] = 4 * n + 2;
			indexes[6 * n + 5] = 4 * n + 3;
		}
		mIndexBuffer->Unlock();
	}

	void GrassLoader::render(double _elapsedTime)
	{
		_setBuffers();
		if (mNumberPrimitives == 0 || mNumberVertices == 0)
			return;

		LPD3DXEFFECT shader = CoreEngine::getInstance()->getShaderEffect();
		IDirect3DDevice9* device = CoreEngine::getInstance()->getDeviceInterface();
		
		D3DXMATRIX matVP = CoreEngine::getInstance()->getCamera()->_getMatrixView() * CoreEngine::getInstance()->getCamera()->_getMatrixProjection();
		
		if (shader->SetTechnique(shader->GetTechniqueByName("Billboard")) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader technique");
		if (shader->SetMatrix(shader->GetParameterByName(NULL, "gViewProj"), &matVP) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader parameter");
		if (shader->SetTexture(shader->GetParameterByName(NULL, "gMeshTexture"), mBillboardTexture) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader parameter ");
		shader->CommitChanges();

		device->SetVertexDeclaration(mVertexDeclaration);
		device->SetStreamSource(0, mVertexBuffer, 0, sizeof(FE_BILLBOARD));
		device->SetIndices(mIndexBuffer);
		
		unsigned int cPasses, iPass;
		shader->Begin(&cPasses, 0);
		for (iPass= 0; iPass< cPasses; ++iPass)
		{
			shader->BeginPass(iPass);
			CoreEngine::getInstance()->getDeviceInterface()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumberVertices, 0, mNumberPrimitives);
			shader->EndPass();
		}
		shader->End();
	}

} // namespace FEngine
